import os
import shutil
from concurrent.futures import ThreadPoolExecutor
import subprocess


def parse_condition(condition, mode=None):
    """
    根据提供的条件字符串生成一个函数，该函数接受一个名称参数，并根据条件字符串评估是否满足条件。

    参数:
    - condition: 字符串类型的条件表达式，使用'and'、'or'逻辑运算符连接。如
    "'png' or 'ppt' or 'xls' or 'txt' or ('str1' and 'str2')"
    执行命令："'png' in name or 'ppt' in name or 'xls' in name or 'txt' in name or ('str1' in name and 'str2'in name)"
    - mode: 字符串，指定条件的模式，默认为'in'，可选值为'in'或'not in'。

    返回:
    - 如果condition为None，返回一个始终返回True的lambda函数。
    - 否则，返回一个lambda函数，它接受一个名称参数，并使用条件表达式评估是否满足条件。
    """
    if mode is None:
        mode = "in"
    else:
        if mode not in ['in', 'not in']:
            raise ValueError("mode must be 'in' or 'not in'")
    # 如果条件为空，则任何名称都视为满足条件
    if condition is None:
        return lambda name: True

    def evaluate_condition(name, condition):
        """
        评估名称是否满足给定的条件表达式。

        参数:
        - name: 需要评估的名称字符串。
        - condition: 条件表达式字符串。

        返回:
        - 根据条件表达式评估的结果。
        """
        # 替换条件中的逻辑运算符为对名称的成员运算符，以支持灵活的条件判断
        condition = condition.rstrip()
        condition = condition.replace("and", f"{mode} name and")
        condition = condition.replace("or", f"{mode} name or")
        condition = condition.replace(")", f"{mode} name)")
        if condition[-1] != ")" or f" {mode} name " not in condition:
            condition += f" {mode} name "
        # 使用eval执行替换后的条件表达式进行评估
        return eval(condition)

    # 返回一个lambda函数，用于评估名称是否满足条件
    return lambda name: evaluate_condition(name, condition)


def copy_files_with_complex_conditions(src_dir, dst_dir, include_condition=None, exclude_condition=None,
                                       overwrite=False, max_workers=None):
    """
    复制指定目录下符合复杂筛选条件的文件及文件夹到另一个目录，保持原有文件结构。

    :param src_dir: 源目录路径
    :param dst_dir: 目标目录路径
    :param include_condition: 文件名必须满足的条件字符串，例如 "'png' or 'ppt' or 'xls' or 'txt' or ('str1' and 'str2')"
    :param exclude_condition: 文件名不得满足的条件字符串，例如 "'png' or 'ppt' or 'xls' or 'txt' or ('str1' and 'str2')"
    :param overwrite: 是否覆盖已存在的文件，默认为 False
    """
    # 确保目标目录存在
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)

    # 解析条件字符串
    include_func = parse_condition(include_condition)
    exclude_func = parse_condition(exclude_condition, mode='not in')

    total_files = 0
    copied_files = 0

    # 统计总文件数
    for root, dirs, files in os.walk(src_dir):
        total_files += len(files)
    print(f"Total files to process: {total_files}")
    src_files = []
    dst_files = []
    for root, dirs, files in os.walk(src_dir):
        # 计算当前目录相对于源目录的相对路径
        rel_path = os.path.relpath(root, src_dir)
        dst_root = os.path.join(dst_dir, rel_path)

        # 遍历文件
        if_makedirs = False
        for file in files:
            if include_func(file) and exclude_func(file):
                # 创建目标目录中的对应子目录
                if not if_makedirs:
                    if not os.path.exists(dst_root):
                        os.makedirs(dst_root)
                        if_makedirs = True
                src_file = os.path.join(root, file)
                dst_file = os.path.join(dst_root, file)

                # 根据overwrite参数决定是否覆盖
                if overwrite or not os.path.exists(dst_file):

                    src_files.append(src_file)
                    dst_files.append(dst_file)
                    if len(src_files) > 800:
                        with ThreadPoolExecutor(max_workers=max_workers) as executor:
                            executor.map(shutil.copy2, src_files, dst_files)
                        src_files.clear()
                        dst_files.clear()
                    # shutil.copy2(src_file, dst_file)  # 使用copy2以保留元数据
                    copied_files += 1
                    # print(f"Copied: {src_file} -> {dst_file}")
                else:
                    pass
                    # print(f"Skipped (already exists): {src_file}")

    if src_files:
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            executor.map(shutil.copy2, src_files, dst_files)
        src_files.clear()
        dst_files.clear()
    print(f"Copying completed. Total files processed: {total_files}, Copied files: {copied_files}")


# 示例调用
src_directory = r'G:\working\yaliecang\analyse'
dst_directory = r'F:\压裂舱参数分析结果数据'

# temp_directory = src_directory
# src_directory = dst_directory
# dst_directory = temp_directory

include_condition = "'png' or 'ppt' or 'xls' or 'txt' or 'opju'"
# include_condition="'.cae'"
copy_files_with_complex_conditions(src_directory, dst_directory, include_condition, overwrite=False)
